﻿LocusClient = function() {

	var watermark = 0;
    var eventPostUrl = null;
	var eventHandlers = {};
	var boundCollections = {};

	this.RegisterEventHandler = function(source, event, handler)
	{
		eventHandlers[source + '|' + event] = handler;
	}
	
	this.BindCollection = function(source, obj)
	{
		boundCollections[source] = obj;
	}
	
	this.SendMessage = function(msg)
	{
		// TODO: need to handle network/server errors
		
        $.post(eventPostUrl,
            { event: msg.Type, msg: JSON.stringify(msg) });
	}

    this.StreamingEventCallback = function(msg) {
    
    	var src = msg.Source;
    	var event = msg.Type;

		// update watermark	(assume monotonic)
    	watermark = msg.Watermark;
    
    	//
    	// update bound collection (if any)
    	//
    	
    	var collection = boundCollections[src]
    	
    	if (collection)
    	{
    		if (msg.Name)
    		{
    			collection[msg.Name] = msg;
    		}
    		else
    		{
    			collection[msg.Watermark] = msg;
    		}
    		
    		// poke data-binding handler, if one is registered
    		if (collection.onchange)
    		{
    			collection.onchange();
    		}
    	}
    
    	//
    	// route to registered handler (if any)
    	//
    	 
    	var handler = eventHandlers[src + '|' + event];
    	
    	if (handler)
    	{
    		handler(msg);
    	}
    		
    }

    this.Connect = function(id) {
    
        eventPostUrl = "ClientActions.ashx?id=" + id;
        endpointUrl = "ClientEventStream.ashx?watermark=" + watermark + "&id=" + id;

        if ($.browser.mozilla || $.browser.safari) {

            //
            // set up XHR streaming
            //

            var READY_STATE_RECEIVING = 3;
            var EVENT_DELIMITER = ");\r\n";

            var curOffset = 0;

            document.__LocusClientInstance = this;

            var xhr = new XMLHttpRequest();
            xhr.open("GET", endpointUrl, true);
            xhr.onreadystatechange = function() {

                // on ffx and safari, this event fires with each incoming block
                if (xhr.readyState == READY_STATE_RECEIVING) {

                    // seek backwards from the end to find the last complete event
                    var buf = xhr.responseText;
                    var endIdx = buf.lastIndexOf(EVENT_DELIMITER, buf.length);

                    if (endIdx != -1) {

                        endIdx = endIdx + EVENT_DELIMITER.length - 1;

                        var data = buf.slice(curOffset, endIdx);
                        curOffset = endIdx + 1;

                        // TODO: handle restart once data size becomes very large

                        // fire events
                        eval(data);
                    }
                }

            };
            xhr.send(null);
        }
        else if ($.browser.msie) {

            //
            // set up iframe with HTMLFILE container
            //

            // create a synthetic empty HTML doc that shares our domain so that we can script it
            var ax = new ActiveXObject("htmlfile");
            ax.open();
            ax.write("<html><script>");
            ax.write("document.domain='" + document.domain + "';");
            ax.write("</script></html>");

            // add our event callback to the parent-window object
            ax.parentWindow.__LocusClientInstance = this;
            ax.close();

            // inject our streaming iframe (the parent will be shielded from loading events)
            var ifWrapper = ax.createElement("div");
            ax.appendChild(ifWrapper);
            ifWrapper.innerHTML = "<iframe src='" + endpointUrl + "'></iframe>";
        }
        else {

            //
            // unsupported browser: fall back to ugly iframe streaming
            //

            $(document.body).append("<iframe style='width: 0px; height: 0px; border: 0px' id='__locusClientEventFrame' src='ClientEvents.ashx'></iframe>");
        }
    }
}
